package sysUser

import (
	"context"
	"errors"
	"fmt"
	v1 "gf-admin/api/system/v1"
	"gf-admin/internal/consts"
	"gf-admin/internal/dao"
	"gf-admin/internal/logic/casbin"
	"gf-admin/internal/model"
	"gf-admin/internal/model/do"
	"gf-admin/internal/model/entity"
	"gf-admin/internal/service"
	"gf-admin/utility"
	lib "gf-admin/utility/lib"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/gogf/gf/v2/util/grand"
	"github.com/google/uuid"
	"github.com/mojocn/base64Captcha"
)

type configJsonBody struct {
	Id            string
	CaptchaType   string
	VerifyValue   string
	DriverAudio   *base64Captcha.DriverAudio
	DriverString  *base64Captcha.DriverString
	DriverChinese *base64Captcha.DriverChinese
	DriverMath    *base64Captcha.DriverMath
	DriverDigit   *base64Captcha.DriverDigit
}

type sSysUser struct {
	casBinUserPrefix string //CasBin 用户id前缀
}

func New() *sSysUser {

	return &sSysUser{
		casBinUserPrefix: "u_",
	}
}

func init() {
	service.RegisterSysUser(New())
}

func (s *sSysUser) GetCasBinUserPrefix() string {
	return s.casBinUserPrefix
}

func (s *sSysUser) GetAdminRules(ctx context.Context, userInfo *model.LoginUserRes) (sysMenuList []*model.UserMenus, permissions []string, err error) {
	//先判断是否为超级管理员,1为超级管理员,0不是
	if userInfo.UserName == "admin" {
		//获取超级管理员所有菜单
		permissions = []string{"*/*/*"}
		sysMenuList, err = service.SysMenu().GetAllMenuList(ctx)
		if err != nil {
			return nil, nil, err
		}
		return
	} else {
		//获取角色列表
		allRoles, err1 := service.SysRole().GetRoleList(ctx)
		if err1 != nil {
			return nil, nil, err1
		}
		//获取当前登录的用户角色,并且筛选出来.绑定的角色
		err2, userInfo := service.CtxInfo().GetUserInfo(ctx)
		if err2 != nil {
			return nil, nil, err2
		}
		roles, err3 := s.GetAdminRole(ctx, userInfo.Id, allRoles)
		if err3 != nil {
			return nil, nil, err3
		}
		name := make([]string, len(roles))
		roleIds := make([]uint, len(roles))
		for k, v := range roles {
			name[k] = v.Name
			roleIds[k] = v.Id
		}
		sysMenuList, err = s.GetAdminMenusByRoleIds(ctx, roleIds)
		if err != nil {
			return nil, nil, err
		}
		permissions, err = s.GetPermissions(ctx, roleIds)
		if err != nil {
			return nil, nil, err
		}

	}

	return
}

func (s *sSysUser) GetPermissions(ctx context.Context, roleIds []uint) (userButtons []string, err error) {
	enforcer, err := casbin.CasbinEnforcer(ctx)
	if err != nil {
		return nil, errors.New("GetPermissions初始化casbin失败:" + err.Error())
	}

	menuIds := map[int64]int64{}
	for _, roleId := range roleIds {
		//查询当前权限
		gp := enforcer.GetFilteredPolicy(0, gconv.String(roleId))
		for _, p := range gp {
			mid := gconv.Int64(p[1])
			menuIds[mid] = mid
		}
	}
	//获取所有开启的按钮
	allButtons, err := service.SysMenu().GetIsButtonList(ctx)
	if err != nil {
		return nil, err
	}
	userButtons = make([]string, 0, len(allButtons))
	for _, button := range allButtons {
		if _, ok := menuIds[gconv.Int64(button.Id)]; gstr.Equal(button.Conditions, "nocheck") || ok {
			userButtons = append(userButtons, button.Name)
		}
	}

	return userButtons, nil

}

func (s *sSysUser) GetAdminMenusByRoleIds(ctx context.Context, roleIds []uint) (menus []*model.UserMenus, err error) {
	//获取菜单角色id
	enforcer, err1 := casbin.CasbinEnforcer(ctx)
	if err1 != nil {
		return nil, errors.New("获取菜单角色id casbin初始化失败:" + err1.Error())
	}
	menusIds := make(map[int64]int64)
	for _, roleId := range roleIds {
		//查询当前权限
		gp := enforcer.GetFilteredPolicy(0, gconv.String(roleId))
		for _, p := range gp {
			mid := gconv.Int64(p[1])
			menusIds[mid] = mid
		}
	}

	//获取菜单栏
	allMenus, err2 := service.SysMenu().GetIsMenuList(ctx, false)
	if err2 != nil {
		return nil, err2
	}
	menus = make([]*model.UserMenus, 0, len(allMenus))
	for _, v := range allMenus {
		if _, ok := menusIds[gconv.Int64(v.Id)]; gstr.Equal(v.Conditions, "nocheck") || ok {
			var roleMenu *model.UserMenu
			roleMenu = service.SysMenu().SetMenuData(roleMenu, v)
			menus = append(menus, &model.UserMenus{UserMenu: roleMenu})
		}
	}
	menus = service.SysMenu().GetMenusTree(menus, 0)
	return menus, nil
}

//获取用户角色
func (s *sSysUser) GetAdminRole(ctx context.Context, userId uint64, allRoleList []*model.SysRole) (roles []*model.SysRole, err error) {
	var roleIds []uint
	roleIds, err = s.GetAdminRoleIds(ctx, userId)
	if err != nil {
		return nil, err
	}
	roles = make([]*model.SysRole, 0, len(allRoleList))
	for _, v := range allRoleList {
		for _, id := range roleIds {
			if id == v.Id {
				roles = append(roles, v)
			}
		}
		if len(roles) == len(roleIds) {
			break
		}
	}
	return
}

//获取用户校色Ids
func (s *sSysUser) GetAdminRoleIds(ctx context.Context, userId uint64) (roleIds []uint, errs error) {
	enforcer, err := casbin.CasbinEnforcer(ctx)
	if err != nil {
		return nil, errors.New("casbin实例化出错:" + err.Error())
	}
	//查询关联角色规则
	groupPolicy := enforcer.GetFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, userId))
	if len(groupPolicy) > 0 {
		roleIds = make([]uint, len(groupPolicy))
		//得到角色id的切片
		for k, v := range groupPolicy {
			roleIds[k] = gconv.Uint(v[1])
		}
	}
	return
}

func (s *sSysUser) FindUserNameInfo(ctx context.Context, userName string) (userInfo *entity.SysUser, errs error) {
	err := dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().UserName, userName).Scan(&userInfo)
	if err != nil {
		return nil, errors.New("登录时查询数据库出错" + err.Error())
	}
	return
}

func (s *sSysUser) FindUserIdInfo(ctx context.Context, userId int, token string) (mLoginUserRes *model.LoginUserRes, errs error) {
	err := dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().Id, userId).Scan(&mLoginUserRes)
	if err != nil {
		return nil, err
	}
	mLoginUserRes.TokenType = "Bearer"
	mLoginUserRes.AccessToken = token
	return
}

func (s *sSysUser) GetCaptcha() (out *model.UserCaptchaOutput, err error) {
	//生成base64 验证码字符串返回出去给前端
	id, b64s, err := DriverDigitFunc()
	if err != nil {
		return nil, errors.New("验证码生成失败：" + err.Error())
	}
	return &model.UserCaptchaOutput{
		Id:        id,
		RequestId: "",
		Data:      b64s,
	}, nil
}

func DriverDigitFunc() (id, b64s string, err error) {

	e := configJsonBody{}
	e.Id = uuid.New().String()
	e.DriverDigit = base64Captcha.NewDriverDigit(80, 240, 4, 0.7, 80)
	driver := e.DriverDigit
	cap := base64Captcha.NewCaptcha(driver, base64Captcha.DefaultMemStore)
	return cap.Generate()
}

// 获取用户列表
func (s *sSysUser) List(ctx context.Context, req *v1.UserSearchReq) (total interface{}, userList []*model.SysUser, err error) {
	m := dao.SysUser.Ctx(ctx)
	if req.KeyWords != "" {
		keyWords := "%" + req.KeyWords + "%"
		m = m.Where("user_name like ? or nick_name like ?", keyWords, keyWords)
	}
	if req.DeptId != "" {
		deptIds, err1 := s.getSearchDeptIds(ctx, gconv.Int64(req.DeptId))
		if err1 != nil {
			return 0, nil, err1
		}
		m = m.Where("dept_id in (?)", deptIds)
	}
	if req.Status != "" {
		m = m.Where("user_status", gconv.Int(req.Status))
	}
	if req.Mobile != "" {
		m = m.Where("mobile like ?", "%"+req.Mobile+"%")
	}
	if len(req.DateRange) > 0 {
		m = m.Where("created_at >=? AND created_at <=?", req.DateRange[0], req.DateRange[1])
	}
	if req.PageSize == 0 {
		req.PageSize = consts.PageSize
	}
	if req.PageNum == 0 {
		req.PageNum = 1
	}
	totals, err2 := m.Count()
	if err2 != nil {
		return nil, nil, err2
	}
	err3 := m.FieldsEx(dao.SysUser.Columns().PassWord, dao.SysUser.Columns().UserSalt).
		Page(req.PageNum, req.PageSize).Order("id asc").Scan(&userList)
	if err3 != nil {
		return nil, nil, err3
	}
	return totals, userList, nil
}

//删除用户
func (s *sSysUser) Delete(ctx context.Context, ids []int) (err error) {
    for _, item := range ids {
        if item == 1 {
            return errors.New("抱歉超级管理是不允许删除的")
            }
	}
    var adminId []int
	for _, v := range ids {
		if v != 1 {
			adminId = append(adminId, v)
		}
	}
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		err = g.Try(ctx, func(ctx context.Context) {
			_, err = dao.SysUser.Ctx(ctx).TX(tx).Where(dao.SysUser.Columns().Id+" in(?)", adminId).Delete()
			lib.ErrIsNil(ctx, err, "删除用户失败")
			//删除对应权限
			enforcer, e := casbin.CasbinEnforcer(ctx)
			lib.ErrIsNil(ctx, e)
			for _, v := range ids {
				enforcer.RemoveFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, v))
			}
			//删除用户对应的岗位
			_, err = dao.SysUserPost.Ctx(ctx).TX(tx).Delete(dao.SysUserPost.Columns().UserId+" in (?)", adminId)
			lib.ErrIsNil(ctx, err, "删除用户的岗位失败")
		})
		return err
	})
	return
}

// 获取编辑用户信息
func (s *sSysUser) GetEditUser(ctx context.Context, id uint64) (res *v1.UserGetEditRes, err error) {
	res = new(v1.UserGetEditRes)
	err = g.Try(ctx, func(ctx context.Context) {
		//获取用户信息
		res.Content.User, err = s.GetUserInfoById(ctx, id)
		lib.ErrIsNil(ctx, err)
		//获取已选择的角色信息
		res.Content.CheckedRoleIds, err = s.GetAdminRoleIds(ctx, id)
		lib.ErrIsNil(ctx, err)
		res.Content.CheckedPosts, err = service.SysPost().GetPostIds(ctx, id)
		lib.ErrIsNil(ctx, err)
	})
	return
}

//修改用户信息
func (s *sSysUser) Edit(ctx context.Context, req *v1.UserEditReq) (err error) {
	err = s.UserNameOrMobileExists(ctx, "", req.Mobile, req.UserId)
	if err != nil {
		return
	}
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		err = g.Try(ctx, func(ctx context.Context) {
			_, err = dao.SysUser.Ctx(ctx).TX(tx).WherePri(req.UserId).Update(do.SysUser{
				Mobile:     req.Mobile,
				NickName:   req.NickName,
				UserStatus: req.Status,
				UserEmail:  req.Email,
				Sex:        req.Sex,
				DeptId:     req.DeptId,
				Remarks:    req.Remark,
				IsAdmin:    req.IsAdmin,
			})
			lib.ErrIsNil(ctx, err, "修改用户信息失败")
			//设置用户所属角色信息
			err = s.EditUserRole(ctx, req.RoleIds, req.UserId)
			lib.ErrIsNil(ctx, err, "设置用户权限失败")
			err = s.AddUserPost(ctx, tx, req.PostIds, req.UserId)
			lib.ErrIsNil(ctx, err)
		})
		return err
	})
	return
}

//添加用户
func (s *sSysUser) Add(ctx context.Context, req *v1.UserAddReq) (err error) {
	err = s.UserNameOrMobileExists(ctx, req.UserName, req.Mobile)
	if err != nil {
		return
	}
	req.UserSalt = grand.S(10)
	req.Password = utility.EncryptPassword(req.Password, req.UserSalt)
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		err = g.Try(ctx, func(ctx context.Context) {
			userId, e := dao.SysUser.Ctx(ctx).TX(tx).InsertAndGetId(do.SysUser{
				UserName:   req.UserName,
				Mobile:     req.Mobile,
				NickName:   req.NickName,
				PassWord:   req.Password,
				UserSalt:   req.UserSalt,
				UserStatus: req.Status,
				UserEmail:  req.Email,
				Sex:        req.Sex,
				DeptId:     req.DeptId,
				Remarks:    req.Remark,
				IsAdmin:    req.IsAdmin,
			})
			lib.ErrIsNil(ctx, e, "添加用户失败")
			err = service.SysRole().AddUserRole(ctx, req.RoleIds, userId)
			lib.ErrIsNil(ctx, err, "设置用户权限失败")
			e = s.AddUserPost(ctx, tx, req.PostIds, userId)
			lib.ErrIsNil(ctx, e)
		})
		return err
	})
	return
}

// EditUserRole 修改用户角色信息
func (s *sSysUser) EditUserRole(ctx context.Context, roleIds []int64, userId int64) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		enforcer, e := casbin.CasbinEnforcer(ctx)
		lib.ErrIsNil(ctx, e)

		//删除用户旧角色信息
		enforcer.RemoveFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, userId))
		for _, v := range roleIds {
			_, err = enforcer.AddGroupingPolicy(fmt.Sprintf("%s%d", s.casBinUserPrefix, userId), gconv.String(v))
			lib.ErrIsNil(ctx, err)
		}
	})
	return
}

// AddUserPost 添加用户岗位信息
func (s *sSysUser) AddUserPost(ctx context.Context, tx gdb.TX, postIds []int64, userId int64) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		//删除旧岗位信息
		_, err = dao.SysUserPost.Ctx(ctx).TX(tx).Where(dao.SysUserPost.Columns().UserId, userId).Delete()
		lib.ErrIsNil(ctx, err, "设置用户岗位失败")
		if len(postIds) == 0 {
			return
		}
		//添加用户岗位信息
		data := g.List{}
		for _, v := range postIds {
			data = append(data, g.Map{
				dao.SysUserPost.Columns().UserId: userId,
				dao.SysUserPost.Columns().PostId: v,
			})
		}
		_, err = dao.SysUserPost.Ctx(ctx).TX(tx).Data(data).Insert()
		lib.ErrIsNil(ctx, err, "设置用户岗位失败")
	})
	return
}

func (s *sSysUser) UserNameOrMobileExists(ctx context.Context, userName, mobile string, id ...int64) error {
	user := (*entity.SysUser)(nil)
	err := g.Try(ctx, func(ctx context.Context) {
		m := dao.SysUser.Ctx(ctx)
		if len(id) > 0 {
			m = m.Where(dao.SysUser.Columns().Id+" != ", id)
		}
		m = m.Where(fmt.Sprintf("%s='%s' OR %s='%s'",
			dao.SysUser.Columns().UserName,
			userName,
			dao.SysUser.Columns().Mobile,
			mobile))
		err := m.Limit(1).Scan(&user)
		lib.ErrIsNil(ctx, err, "获取用户信息失败")
		if user == nil {
			return
		}
		if user.UserName == userName {
			lib.ErrIsNil(ctx, gerror.New("用户名已存在"))
		}
		if user.Mobile == mobile {
			lib.ErrIsNil(ctx, gerror.New("手机号已存在"))
		}
	})
	return err
}

//通过Id获取用户信息
func (s *sSysUser) GetUserInfoById(ctx context.Context, id uint64, withPwd ...bool) (user *model.SysUser, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		if len(withPwd) > 0 && withPwd[0] {
			//用户用户信息
			err = dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().Id, id).Scan(&user)
		} else {
			//用户用户信息
			err = dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().Id, id).
				FieldsEx(dao.SysUser.Columns().PassWord, dao.SysUser.Columns().UserSalt).Scan(&user)
		}
		lib.ErrIsNil(ctx, err, "获取用户数据失败")
	})
	return
}

func (s *sSysUser) getSearchDeptIds(ctx context.Context, deptId int64) (deptIds []uint64, errs error) {
	var deptList = make([]*model.SysDeptList, 0)
	err1 := dao.SysDept.Ctx(ctx).Scan(&deptList)
	if err1 != nil {
		return nil, errors.New("查询数据库部门数据失败:" + err1.Error())
	}
	deptWithChildren := service.SysDept().FindSonByParentId(deptList, deptId)
	deptIds = make([]uint64, len(deptWithChildren))
	for k, v := range deptWithChildren {
		deptIds[k] = gconv.Uint64(v.DeptId)
	}
	deptIds = append(deptIds, gconv.Uint64(deptId))
	return
}

//获取多个用户角色,部门信息
func (s *sSysUser) GetUsersRoleDept(ctx context.Context, userList []*model.SysUser) (userListRes []*model.SysUserRoleDeptRes, err error) {
	//获取角色列表
	rolelist, err1 := service.SysRole().GetRoleList(ctx)
	if err1 != nil {
		return nil, err1
	}
	//获取部门列表
	deptList, err2 := service.SysDept().GetList(ctx, &v1.DeptSearchReq{IsType: 1})
	if err2 != nil {
		return nil, err2
	}
	users := make([]*model.SysUserRoleDeptRes, len(userList))
	for k, u := range userList {
		var dept *model.SysDept
		users[k] = &model.SysUserRoleDeptRes{
			SysUser: u,
		}

		for _, deptItem := range deptList {
			if u.DeptId == gconv.Uint(deptItem.DeptId) {
				dept = deptItem
			}
		}

		users[k].Dept = dept
		roles, err3 := s.GetAdminRole(ctx, u.Id, rolelist)
		if err3 != nil {
			return nil, err3
		}
		for _, r := range roles {
			users[k].RoleInfo = append(users[k].RoleInfo, &model.SysUserRoleInfoRes{
				RoleId: r.Id,
				Name:   r.Name,
			})
		}
	}
	return users, nil
}

// GetParams 获取岗位信息和角色信息
func (c *sSysUser) GetParams(ctx context.Context, req *v1.UserGetParamsReq) (ress *v1.UserGetParamsRes, errs error) {
	res := &v1.UserGetParamsRes{Content: &v1.UserGetParams{}}
	roleList, err1 := service.SysRole().GetRoleList(ctx)
	if err1 != nil {
		return
	}
	postList, err2 := service.SysPost().GetUsedPost(ctx)
	if err2 != nil {
		return nil, err2
	}
	res.Content.RoleList = roleList
	res.Content.Posts = postList
	return res, nil
}

//重置用户密码
// ResetUserPwd 重置用户密码
func (s *sSysUser) ResetUserPwd(ctx context.Context, req *v1.UserResetPwdReq) (err error) {
	salt := grand.S(10)
	// d6e847d36a9956259895c6af713e29f6
	password := utility.EncryptPassword(req.Password, salt)

	err = g.Try(ctx, func(ctx context.Context) {

		lib.ErrIsNil(ctx, err, "")
		_, err = dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().Id, req.Id).Update(g.Map{
			dao.SysUser.Columns().UserSalt: salt,
			dao.SysUser.Columns().PassWord: password,
		})
		lib.ErrIsNil(ctx, err, "重置用户密码失败")
	})
	return
}
